//   Copyright 2012,2013 Vaughn Vernon
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package com.saasovation.collaboration.port.adapter.persistence.view;

import com.saasovation.collaboration.domain.model.forum.PostContentAltered;
import com.saasovation.collaboration.domain.model.forum.PostedToDiscussion;
import com.saasovation.common.event.sourcing.DispatchableDomainEvent;
import com.saasovation.common.event.sourcing.EventDispatcher;
import com.saasovation.common.port.adapter.persistence.AbstractProjection;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import java.util.List;
import java.util.Map;

public class MySQLPostProjection extends AbstractProjection
        implements EventDispatcher {

    private final NamedParameterJdbcTemplate jdbcTemplate;

    private static final Class<?>[] understoodEventTypes = {
            PostContentAltered.class,
            PostedToDiscussion.class
    };

    public MySQLPostProjection(EventDispatcher aParentEventDispatcher,
                               NamedParameterJdbcTemplate jdbcTemplate) {
        super();
        this.jdbcTemplate = jdbcTemplate;
        aParentEventDispatcher.registerEventDispatcher(this);
    }

    @Override
    public void dispatch(DispatchableDomainEvent aDispatchableDomainEvent) {
        this.projectWhen(aDispatchableDomainEvent);
    }

    @Override
    public void registerEventDispatcher(EventDispatcher anEventDispatcher) {
        throw new UnsupportedOperationException("Cannot register additional dispatchers.");
    }

    @Override
    public boolean understands(DispatchableDomainEvent aDispatchableDomainEvent) {
        return this.understandsAnyOf(
                aDispatchableDomainEvent.domainEvent().getClass(),
                understoodEventTypes);
    }

    protected void when(PostContentAltered anEvent) {
        String sql = "update tbl_vw_post " +
                "set body_text = :bodyText, " +
                "subject = :subject, " +
                "changed_on = :changedOn " +
                "where tenant_id = :tenantId and forum_id = :forumId";
        MapSqlParameterSource params = new MapSqlParameterSource()
                .addValue("bodyText", anEvent.bodyText())
                .addValue("subject", anEvent.subject())
                .addValue("changedOn", anEvent.occurredOn())
                .addValue("tenantId", anEvent.tenant().id())
                .addValue("forumId", anEvent.postId().id());
        jdbcTemplate.update(sql, params);
    }

    protected void when(PostedToDiscussion anEvent) {
        String existsSql = "select post_id from tbl_vw_post " +
                "where tenant_id = :tenantId and post_id = :postId";
        MapSqlParameterSource existsParams = new MapSqlParameterSource()
                .addValue("tenantId", anEvent.tenant().id())
                .addValue("postId", anEvent.postId().id());
        List<Map<String, Object>> result = jdbcTemplate.queryForList(existsSql, existsParams);
        // idempotent operation
        if (!result.isEmpty()) {
            return;
        }
        String insertSql = "insert into tbl_vw_post( " +
                "post_id, " +
                "author_email_address, author_identity, author_name, " +
                "body_text, changed_on, created_on, " +
                "discussion_id, forum_id, reply_to_post_id, " +
                "subject, tenant_id" +
                ") values(" +
                ":postId, " +
                ":authorEmail, :authorIdentity, :authorName, " +
                ":bodyText, :changedOn, :createdOn, " +
                ":discussionId, :forumId, :replyToPostId, " +
                ":subject, :tenantId)";
        java.sql.Timestamp timestamp = new java.sql.Timestamp(anEvent.occurredOn().getTime());
        MapSqlParameterSource insertParams = new MapSqlParameterSource()
                .addValue("postId", anEvent.postId().id())
                .addValue("authorEmail", anEvent.author().emailAddress())
                .addValue("authorIdentity", anEvent.author().identity())
                .addValue("authorName", anEvent.author().name())
                .addValue("bodyText", anEvent.bodyText())
                .addValue("changedOn", timestamp)
                .addValue("createdOn", timestamp)
                .addValue("discussionId", anEvent.discussionId().id())
                .addValue("forumId", anEvent.forumId().id())
                .addValue("replyToPostId", anEvent.replyToPost() == null ? null : anEvent.replyToPost().id())
                .addValue("subject", anEvent.subject())
                .addValue("tenantId", anEvent.tenant().id());
        jdbcTemplate.update(insertSql, insertParams);
    }
}
